﻿using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip;

namespace CalcSharp.Core.IO
{
    public class CFSEntry
    {
        public compfs value;
        public string desc;
    }

    public class CFsManager
    {
        private List<CFSEntry> Engine;

        public CFsManager()
        {
            this.Engine = new List<CFSEntry>();
        }

        public int Count
        {
            get { return this.Engine.Count; }
        }

        public void Clear()
        {
            this.Engine.Clear();
        }

        public bool isset(string name)
        {
            int i;
            name = name.ToLower(CultureInfo.CurrentCulture);
            bool res = false;
            for (i = 0; i < Engine.Count; i++)
            {
                if (Engine[i].desc == name)
                {
                    res = true;
                    break;
                }
            }
            return res;
        }

        public void unset(string name)
        {
            string test = Regex.Replace(name, @"^\*[a-z1-9_-]+", "-ok-");
            if (test != "-ok-") throw new CSException("Incorrect variable name: " + name);
            int i, index;
            index = -1;
            for (i = 0; i < Engine.Count; i++)
            {
                if (Engine[i].desc == name)
                {
                    index = i;
                    break;
                }
            }
            if (index != -1) Engine.RemoveAt(index);
        }

        public void unsetAll()
        {
            while (Engine.Count > 0)
            {
                Engine[0].value.Unmount();
                Engine.RemoveAt(0);
            }
        }

        public int Add(CFSEntry item)
        {
            Engine.Add(item);
            return Engine.Count;
        }

        public CFSEntry this[int i]
        {
            get { return this.Engine[i]; }
        }

        public compfs this[string s]
        {
            get
            {
                s = s.ToLower(CultureInfo.CurrentCulture);
                int i;
                string test = Regex.Replace(s, @"^\*[a-z1-9_-]+", "-ok-");
                if (test != "-ok-") throw new CSException("Incorrect variable name!");
                compfs val = null;
                for (i = 0; i < Engine.Count; i++)
                {
                    if (Engine[i].desc == s)
                    {
                        val = Engine[i].value;
                        break;
                    }
                }
                return val;
            }
            set
            {
                s = s.ToLower(CultureInfo.CurrentCulture);
                int i;
                bool found = false;
                string test = Regex.Replace(s, @"^\*[a-z1-9_-]+", "-ok-");
                if (test != "-ok-") throw new CSException("Incorrect variable s: " + s);
                for (i = 0; i < Engine.Count; i++)
                {
                    if (Engine[i].desc == s)
                    {
                        Engine[i].value = value;
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    CFSEntry tmp = new CFSEntry();
                    tmp.desc = s;
                    tmp.value = value;
                    Engine.Add(tmp);
                }
            }
        }

    }

    enum FsType
    {
        zip
    }

    public class compfs: IDisposable
    {
        private Dictionary<string, long> Aloctable;
        private string Unlock;
        private ZipFile ZData;
        private FsType type;

        public compfs(string Filetoload, string unlockkey)
        {
            if (string.IsNullOrEmpty(Filetoload)) throw new CSException("Filename not specified");
            this.Aloctable = new Dictionary<string, long>();
            this.Unlock = unlockkey;
            if (Filetoload.ToUpper().EndsWith(".ZIP"))
            {
                this.type = FsType.zip;
                this.ZData = new ZipFile(Filetoload);
                if (this.ZData.Count < 1) throw new CSException("Unable to mount file, because it's empty");
                if (this.ZData[0].IsCrypted) this.ZData.Password = unlockkey;

                for (int index = 0; index < ZData.Count; ++index)
                {
                    if (ZData[index].IsFile) Aloctable.Add(ZData[index].Name.ToUpper(), index);
                }
            }
            else throw new CSException("Only ZIP files can be mounted");
        }

        public void Unmount()
        {
            this.Aloctable = null;
            if (this.ZData != null)
            {
                this.ZData.Close();
                this.ZData = null;
            }
        }

        public int FileCount
        {
            get
            {
                return this.Aloctable.Keys.Count;
            }
        }

        public string[] fsentrys
        {
            get
            {
                string[] ret = new string[Aloctable.Keys.Count];
                this.Aloctable.Keys.CopyTo(ret, 0);
                return ret;
            }
        }

        public bool Extractto(Stream dest, string file)
        {
            bool succes = false;
            if (string.IsNullOrEmpty(file)) throw new CSException("Filename must not be empty");
            if (dest == null) throw new CSException("Destination stream must not be null");
            byte[] buffer = new byte[16 * 1024];
            int read;
            if (this.type == FsType.zip)
            {
                if (this.Aloctable[file] == -1) throw new CSException("File not found: " + file);
                Stream input = ZData.GetInputStream(this.Aloctable[file]);
                do
                {
                    read = input.Read(buffer, 0, buffer.Length);
                    dest.Write(buffer, 0, read);
                }
                while (read > 0);
                succes = true;
            }
            return succes;
        }



        #region IDisposable Members

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.Aloctable != null)
                {
                    this.Aloctable.Clear();
                    this.Aloctable = null;
                }
                if (this.ZData != null)
                {
                    this.ZData.Close();
                    this.ZData = null;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
